package tools

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
)

func Post(uri string, params interface{}, data interface{}, header []*Header, client *http.Client) ([]byte, *http.Response, error) {

	var body io.Reader

	if params != nil {
		uri = fmt.Sprintf("%v?%v", uri, interfaceToString(params))
	}

	if data != nil {

		buf := bytes.NewBuffer([]byte{})

		encoder := json.NewEncoder(buf)
		encoder.SetEscapeHTML(false)
		if err := encoder.Encode(data); err != nil {
			return nil, nil, err
		}

		//toString, err := json.Marshal(data)
		//if err != nil {
		//	return nil, nil, err
		//}
		//
		//content := string(toString)
		//content = strings.Replace(content, "\\u003c", "<", -1)
		//content = strings.Replace(content, "\\u003e", ">", -1)
		//fmt.Println(string(buf.Bytes()))
		body = bytes.NewReader(buf.Bytes())
	}

	req, err := http.NewRequest("POST", uri, body)
	if err != nil {
		return nil, nil, err
	}

	if header != nil {
		for _, v := range header {
			req.Header.Set(v.Key, v.Value)
		}
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	results, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	return results, resp, nil
}

func Get(uri string, params interface{}, header []*Header, client *http.Client) ([]byte, *http.Response, error) {

	if params != nil {
		uri = fmt.Sprintf("%v?%v", uri, interfaceToString(params))
	}

	req, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		return nil, nil, err
	}
	if header != nil {
		for _, v := range header {
			req.Header.Set(v.Key, v.Value)
		}
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	results, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	return results, resp, nil
}

func Upload(uri string, filePath string, params interface{}, data interface{}, header []*Header, client *http.Client) ([]byte, *http.Response, error) {

	if params != nil {
		uri = fmt.Sprintf("%v?%v", uri, interfaceToString(params))
	}

	body := new(bytes.Buffer)
	writer := multipart.NewWriter(body)

	file, err := os.Open(filePath)
	if err != nil {
		return nil, nil, err
	}

	part, err := writer.CreateFormFile("filename", file.Name()) // 这里的"filename"是服务器期望的字段名，可能需要调整
	if err != nil {
		return nil, nil, err
	}

	defer file.Close()
	if _, err = io.Copy(part, file); err != nil {
		return nil, nil, err
	}

	if data != nil {
		m, err := structToMap(data)
		if err != nil {
			return nil, nil, err
		}

		for key, value := range m {
			// 此时key和value都是interface{}类型，你可以根据需要再次断言

			switch v := value.(type) {
			case string:
				_ = writer.WriteField(key, v)
			case int:
				_ = writer.WriteField(key, strconv.Itoa(v))
			default:
				results, err := json.Marshal(v)
				if err != nil {
					return nil, nil, err
				}
				_ = writer.WriteField(key, string(results))
			}
		}
	}

	if err = writer.Close(); err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest("POST", uri, body)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Set("Content-Type", writer.FormDataContentType())

	if header != nil {
		for _, v := range header {
			req.Header.Set(v.Key, v.Value)
		}
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	results, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	return results, resp, nil
}

func structToMap(obj interface{}) (map[string]interface{}, error) {
	// 获取obj的反射类型对象
	valueOfObj := reflect.ValueOf(obj)
	// 确保obj是一个结构体
	if valueOfObj.Kind() == reflect.Ptr {
		valueOfObj = valueOfObj.Elem()
	}
	if valueOfObj.Kind() != reflect.Struct {
		return nil, fmt.Errorf("obj is not a struct")
	}

	// 获取结构体类型
	typeOfObj := valueOfObj.Type()
	// 创建一个新的map来存储转换结果
	numFields := valueOfObj.NumField()
	result := make(map[string]interface{}, numFields)

	// 遍历结构体的每个字段
	for i := 0; i < numFields; i++ {
		// 获取字段的反射值
		field := valueOfObj.Field(i)
		// 获取字段名和类型
		fieldName := typeOfObj.Field(i).Name
		// 将字段值转换为interface{}并添加到map中
		result[fieldName] = field.Interface()
	}

	return result, nil
}

func interfaceToString(params interface{}) string {
	refs := reflect.TypeOf(params)
	values := reflect.ValueOf(params)
	results := url.Values{}
	for i := 0; i < refs.NumField(); i++ {
		results.Add(refs.Field(i).Tag.Get("json"), fmt.Sprintf("%v", values.Field(i).Interface()))
	}
	return results.Encode()
}

type Header struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}
